#pragma  once

#include "StdAfx.h"
#include "DDrawH264Play.h"
#include "DDrawYUV\DirectDisplay.h"


#include <stdio.h>
#include <utility>
#include <streams.h>
#include <process.h>






#define NON_INTERLACED_FOR_CIF 1

extern "C" BOOL __cdecl Deinterlace(int SearchEffort, int _src_pitch, int _dst_pitch,
									int _rowsize, const BYTE* _pWeaveSrc, const BYTE* _pWeaveSrcP,
									BYTE* _pWeaveDest, BOOL _TopFirst, const BYTE* _pCopySrc,
									const BYTE* _pCopySrcP, int _FldHeight);

//------------------------DDrawH264Play Implement-------------------------------

DDrawH264Play::DDrawH264Play(UINT fps, UINT width, UINT height, bool isRawYuv, bool isInterlaced) :
#ifdef SUPPORT_H264
	m_decoderSession(0),
#endif
	m_isRawYuv(isRawYuv),
	m_isInterlaced(isInterlaced),
	m_videoWnd(NULL),
	m_demandWidth(width), 
	m_demandHeight(height),             
	m_demandFps(fps),
	m_playThread(NULL),
	m_playThreadCtrlFlag(0),
	m_pauseFlag(false),
	m_hasDecodeOneFrame(false),
	m_decodeWidth(0), 
	m_decodeHeight(0),			
	m_beginNewFrame(true),
	m_bytesForHeader(sizeof(BtlFrameHeader)),
	m_h264DataLen(0),
	m_frameBuf1(NULL),
	m_frameBuf2(NULL),
	m_currFrameBuf(NULL),
	m_videoBufSize(0),
	m_isFileStream(false),
	m_srcFile(NULL),
	m_dataProvider(NULL),
	m_dataProviderParam(0),
	m_playbackSession(0),
	m_drawCallback(NULL),
	m_decodeCallback(NULL),
	m_drawCallbackParam(0),
	m_decodeCallbackParam(0),
	m_lastFpsCountTime(0),
	m_lastActualFps(0),
	m_totalFrame(0),
	m_lastBitrateCountTime(0),
	m_lastBitrate(0),
	m_totalBit(0),
	m_startTime(0),
	m_frameCount(0),
	m_videoWndUpdateFlag(false),
	m_videoRectUpdateFlag(false),
	m_needDisplay(true),
	m_pActiveDirectDisplay(NULL),
	m_dwLastCheckMonitorTick(0)
	//m_frameCallback(NULL)
{
	if (m_demandFps > MAX_FPS)
		m_demandFps = MAX_FPS;

	memset(m_srcName, 0, sizeof(m_srcName));
	memset(&m_header, 0, sizeof(m_header));

	if (m_isRawYuv)
	{
		int bufSize = m_demandWidth * m_demandHeight * 3 / 2 + m_demandHeight * 3;
		m_h264DataBuf = new BYTE[bufSize];
		int yBufSize = m_demandWidth * m_demandHeight;
		memset(m_h264DataBuf, 0, yBufSize);
		memset(m_h264DataBuf + yBufSize, 128, bufSize - yBufSize);
	}
	else
	{
		m_h264DataBuf = new BYTE[MAX_FRAME_SIZE];
	}

	

	EnumDirectDisplay();
}

DDrawH264Play::~DDrawH264Play(void)
{
	StopPlay();

#ifdef SUPPORT_H264
	if (m_decoderSession != 0)
		CloseDecodeSession(m_decoderSession);
#endif

	ReleaseVideoBuffers();

	if (m_srcFile != NULL)
		fclose(m_srcFile);

	delete[] m_h264DataBuf;
	
}

void DDrawH264Play::AllocateVideoBuffers(void)
{
	if (!m_hasDecodeOneFrame)
		return;

	int yBufSize =/* (m_decodeCallback != NULL)
				? (m_demandWidth * m_demandHeight)
				: */(m_decodeWidth * m_decodeHeight);
	m_videoBufSize = yBufSize * 3 / 2;

	if (m_videoBufSize <= 0)
		return;

	m_frameBuf1 = new BYTE[m_videoBufSize];
	if (m_isInterlaced)
		m_currFrameBuf = m_frameBuf1;

	//Fullfill with black frame.
	memset(m_frameBuf1, 0, yBufSize);
	memset(m_frameBuf1 + yBufSize, 128, m_videoBufSize - yBufSize);
	if (m_isInterlaced)
	{
#ifdef NON_INTERLACED_FOR_CIF
		if (m_decodeHeight < 480)
		{
			m_isInterlaced = false;
			return;
		}
#endif
		m_frameBuf2 = new BYTE[m_videoBufSize];
		memset(m_frameBuf2, 0, yBufSize);
		memset(m_frameBuf2 + yBufSize, 128, m_videoBufSize - yBufSize);
	}
}

void DDrawH264Play::ReleaseVideoBuffers(void)
{
	SafeDeleteArray(m_frameBuf1);
	SafeDeleteArray(m_frameBuf2);
	m_currFrameBuf = NULL;
}


#ifdef SUPPORT_H264

//called by PlayThreadImpl
bool DDrawH264Play::DecodeH264Frame()
{
	DWORD t1 = GetTickCount();
	int nSleepTimes = 0;
	int offset = /*(m_decodeCallback != NULL) 
				? abs(m_demandWidth * m_demandHeight)
				: */abs(m_decodeWidth * m_decodeHeight);

	if (m_decoderSession == 0)
		m_decoderSession = CreateDecodeSessionNoSize(1); //stream based decode

	if (m_lastFpsCountTime == 0)
		m_lastFpsCountTime = timeGetTime();

	if (m_frameBuf1 == NULL)
	{
		AllocateVideoBuffers();
	} 
#ifdef NON_INTERLACED_FOR_CIF
	else if (m_hasDecodeOneFrame && m_isInterlaced && m_decodeHeight < 480)
	{
		ReleaseVideoBuffers();
		AllocateVideoBuffers();
	}
#endif
	else if (m_hasDecodeOneFrame 
		&& ((int) m_decodeWidth > m_demandWidth || abs(m_decodeHeight) > m_demandHeight) 
		&& m_decodeCallback == NULL
		&& (int) m_videoBufSize < m_decodeWidth * abs(m_decodeHeight) * 3 / 2)
	{
		ReleaseVideoBuffers();
		AllocateVideoBuffers();
	}

	m_currFrameBuf = m_frameBuf1;
	
	int getframe = 0;
	DWORD startTime = timeGetTime();
	while (!getframe)
	{
		DWORD now = timeGetTime();
		if (now < startTime)
			startTime = now;
		else if (now - startTime > 300 && m_demandWidth != 0)
			return true;

		if (m_h264DataLen <= 0)
		{
			if (!GetH264Data())
			{
				m_h264DataLen = 0;
				CloseDecodeSession(m_decoderSession);
				m_decoderSession = 0;
				m_hasDecodeOneFrame = 0;
                
				return false;
			}
			else if (m_h264DataLen == 0)
			{
				Sleep(6);
				nSleepTimes++;
				continue;
			}
		}

		BYTE *inbuf = m_h264DataBuf;
		//yb added for chengdu uestc,20111109
		//if the frame callback is set, it's just output the frame, don't need decode.
	//	if(m_frameCallback != NULL)
	//	{
	//		m_frameCallback(m_playbackSession, (char*)m_h264DataBuf, m_h264DataLen, m_frameCallbackParam);
	//		m_h264DataLen = 0;
	//		return true;
	//	}
		//else
		//{

		//	DEBUG_TRACE(_T("m_h264DataLen= %d\n", m_h264DataLen));
		//	m_h264DataLen = 0; 
		//	return true;
		//}

		while (m_h264DataLen > 0)
		{
			if (m_beginNewFrame)
			{
				UINT bytesToCopy = min(m_bytesForHeader, m_h264DataLen);
				memcpy(((char *)&m_header) + sizeof(BtlFrameHeader) - m_bytesForHeader, inbuf, bytesToCopy);
				m_bytesForHeader -= bytesToCopy;
				m_h264DataLen -= bytesToCopy;
				inbuf += bytesToCopy;

				if (m_h264DataLen <= m_bytesForHeader)
					break;

				m_beginNewFrame = false;
			}

			BYTE *ybuf = m_currFrameBuf, *ubuf = m_currFrameBuf + offset * 5 / 4, *vbuf = m_currFrameBuf + offset;
            DWORD t4 =GetTickCount();
			
			int decodelen = DecodeData(m_decoderSession, inbuf, m_h264DataLen, &getframe, ybuf, ubuf, vbuf, m_decodeWidth/*((m_decodeCallback != NULL) ? m_demandWidth : m_decodeWidth)*/, 0);//(height < 0) ? 1 : 0);
			DWORD t5 =GetTickCount();
			DEBUG_TRACE(_T("decodeData using time[%d],decodelen[%d]\r\n"),(t5-t4),decodelen);
			if (decodelen < 0)
			{
				m_h264DataLen = 0;
				CloseDecodeSession(m_decoderSession);
				m_decoderSession = 0;
				m_hasDecodeOneFrame = false;
				ReleaseVideoBuffers();

				getframe = 0;

				m_decoderSession = CreateDecodeSessionNoSize(1);
				GetH264Data();
				m_beginNewFrame = true;
				m_bytesForHeader = sizeof(BtlFrameHeader);
			}

			if (decodelen > 0)
			{
				inbuf += decodelen;
				m_h264DataLen -= decodelen;
				m_header.videoLen -= decodelen;
				if (m_header.videoLen <= 0)
				{
					m_beginNewFrame = true;
					m_bytesForHeader = sizeof(BtlFrameHeader);
				}
			}

			if (getframe)
			{
				if (m_isInterlaced)
					std::swap(m_frameBuf1, m_frameBuf2);

				if (m_h264DataLen > 0)
					memmove(m_h264DataBuf, inbuf, m_h264DataLen);

				m_hasDecodeOneFrame = true;
				GetVideoSize(m_decoderSession, (int *) &m_decodeWidth, (int *) &m_decodeHeight);

				//calculate fps

				++m_totalFrame;
				DWORD currTime = timeGetTime();
				if (currTime < m_lastFpsCountTime)
				{
					m_lastFpsCountTime = currTime;
					m_totalFrame = 0;
				}

				if (currTime - m_lastFpsCountTime >= BITRATE_COUNT_INTERVAL)
				{
					m_lastActualFps = (DWORD) (double(m_totalFrame) * 1000.0 / double(currTime - m_lastFpsCountTime) + 0.5);
					m_lastFpsCountTime = currTime;
					m_totalFrame = 0;
				}
				DWORD t2 = GetTickCount();
				DEBUG_TRACE(_T("Decode264Frame using time[%d],sleep times[%d]\r\n"),(t2-t1),nSleepTimes);

                
				//return true;
			}
		}

		//Sleep(0);
	}
    //DWORD t2 = GetTickCount();
	//DEBUG_TRACE(_T("Decode264Frame using time[%d],sleep times[%d]\r\n"),(t2-t1),nSleepTimes);

	return true;
}
#endif

int DDrawH264Play::GetActualVideoSize(int &width, int &height)
{
	if (m_isRawYuv)
	{
		width  = m_demandWidth;
		height = m_demandHeight;

		return 0;
	}

#ifdef SUPPORT_H264
	if (!m_hasDecodeOneFrame)
		return -1;

	width  = m_decodeWidth;
	height = m_decodeHeight;

	return 0;
#else
	return -1;
#endif
}

int DDrawH264Play::GetActualFps() 
{ 	
	return (timeGetTime() - m_lastFpsCountTime > 2 * BITRATE_COUNT_INTERVAL)
			? 0 : m_lastActualFps;
}

int DDrawH264Play::SetDrawCallback(PLAYBACKSESSION session, DRAWCALLBACK drawCallback, long param)
{
	AutoLock lock(m_callbackLock);

	m_playbackSession   = session;
	m_drawCallback      = drawCallback;
	m_drawCallbackParam = param;

	return 0;
}

int DDrawH264Play::SetDecodeCallback(PLAYBACKSESSION session, DECODECALLBACK decodeCallback, long param, bool needDisplay)
{
	AutoLock lock(m_stateLock);

	m_playbackSession     = session;
	m_decodeCallback      = decodeCallback;
	m_decodeCallbackParam = param;
	m_needDisplay         = needDisplay;

	return 0;
}

//int DDrawH264Play::SetFrameCallback(PLAYBACKSESSION session, FrameCALLBACK callback, long param)
//{
//	AutoLock lock(m_stateLock);

//	m_playbackSession     = session;
//	m_frameCallback      = callback;
//	m_frameCallbackParam = param;
	

//	return 0;
//}
void DDrawH264Play::SetOutputWindow(HWND hwnd)
{
	AutoLock lock(m_stateLock);

	m_videoWnd = hwnd;
	m_videoWndUpdateFlag = true;
}

void DDrawH264Play::UpdateVideoWindowRect()
{
	AutoLock lock(m_stateLock);

	m_videoRectUpdateFlag = true;
}

int DDrawH264Play::ChangePlaybackFps(UINT fps)
{
	m_demandFps = fps;

	return 0;
}

int DDrawH264Play::GetSnapshotSize()
{
	if (!m_hasDecodeOneFrame)
		return -1;

	BITMAPINFOHEADER bmpHdr;
	memset(&bmpHdr, 0, sizeof(bmpHdr));
	bmpHdr.biBitCount = 32;

#ifdef SUPPORT_H264

	bmpHdr.biWidth  = m_decodeWidth;
	bmpHdr.biHeight = m_decodeHeight;

#ifdef NEED_DEINTERLACE
	if (bmpHdr.biHeight == 256)
		bmpHdr.biHeight = 240;
#endif //NEED_DEINTERLACE

#else //not SUPPORT_H264

	bmpHdr.biWidth = m_demandWidth;
	bmpHdr.biHeight = m_demandHeight;

#endif //SUPPORT_H264

	return sizeof(BITMAPINFOHEADER) + DIBSIZE(bmpHdr);
}

int DDrawH264Play::DeinterlaceFrames(BYTE *destBuf, int destWidth, int destHeight)
{
	if (!m_isInterlaced)
		return -1;

	int newWidth, newHeight, halfHeight; 
	BYTE *oddFieldYPtr, *evenFieldYPtr, *oddFieldUPtr, *evenFieldUPtr, *oddFieldVPtr, *evenFieldVPtr;
	BYTE *oddFieldYPtrP, *evenFieldYPtrP, *oddFieldUPtrP, *evenFieldUPtrP, *oddFieldVPtrP, *evenFieldVPtrP;
	BYTE *dstY, *dstU, *dstV;
	int dstYBufSize;

	GetVideoSize(m_decoderSession, &newWidth, &newHeight);
	halfHeight   = abs(destHeight) / 2;
	dstYBufSize  = destWidth * abs(destHeight);

	evenFieldYPtr = m_currFrameBuf;
	oddFieldYPtr  = m_currFrameBuf + destWidth * newHeight / 2;
	evenFieldUPtr = m_currFrameBuf + dstYBufSize;
	oddFieldUPtr  = m_currFrameBuf + dstYBufSize + destWidth * abs(newHeight) / 8;
	evenFieldVPtr = m_currFrameBuf + dstYBufSize * 5 / 4;
	oddFieldVPtr  = m_currFrameBuf + dstYBufSize * 5 / 4 + destWidth * abs(newHeight) / 8;

	BYTE *otherFrameBuf = (m_currFrameBuf == m_frameBuf1) ? m_frameBuf2 : m_frameBuf1;
	evenFieldYPtrP = otherFrameBuf;
	oddFieldYPtrP  = otherFrameBuf + destWidth * newHeight / 2;
	evenFieldUPtrP = otherFrameBuf + dstYBufSize;
	oddFieldUPtrP  = otherFrameBuf + dstYBufSize + destWidth * abs(newHeight) / 8;
	evenFieldVPtrP = otherFrameBuf + dstYBufSize * 5 / 4;
	oddFieldVPtrP  = otherFrameBuf + dstYBufSize * 5 / 4 + destWidth * abs(newHeight) / 8;

	dstY = destBuf;
	dstU = destBuf + dstYBufSize;
	dstV = destBuf + dstYBufSize * 5 / 4;

#define IS_TOPFIRST FALSE
#if 1
	Deinterlace(6, destWidth, destWidth, destWidth, evenFieldYPtr, evenFieldYPtrP, dstY, IS_TOPFIRST, oddFieldYPtr, oddFieldYPtrP, halfHeight);
	Deinterlace(6, destWidth / 2, destWidth / 2, destWidth / 2, evenFieldUPtr, evenFieldUPtrP, dstU, IS_TOPFIRST, oddFieldUPtr, oddFieldUPtrP, halfHeight / 2);
	Deinterlace(6, destWidth / 2, destWidth / 2, destWidth / 2, evenFieldVPtr, evenFieldVPtrP, dstV, IS_TOPFIRST, oddFieldVPtr, oddFieldVPtrP, halfHeight / 2);
#else
	int i;
	int halfWidth = destWidth / 2;
	for (i = 0; i < halfHeight; ++i)
	{
		memcpy(dstY, evenFieldYPtr, destWidth);
		dstY += destWidth;
		memcpy(dstY, oddFieldYPtr, destWidth);
		dstY += destWidth;
		evenFieldYPtr += destWidth;
		oddFieldYPtr += destWidth;
	}
	int quaterHeight = halfHeight / 2;
	for (i = 0; i < quaterHeight; ++i)
	{
		memcpy(dstU, evenFieldUPtr, halfWidth);
		dstU += halfWidth;
		evenFieldUPtr += halfWidth;
		memcpy(dstU, oddFieldUPtr, halfWidth);
		dstU += halfWidth;
		oddFieldUPtr += halfWidth;
	}
	for (i = 0; i < quaterHeight; ++i)
	{
		memcpy(dstV, evenFieldVPtr, halfWidth);
		dstV += halfWidth;
		evenFieldVPtr += halfWidth;
		memcpy(dstV, oddFieldVPtr, halfWidth);
		dstV += halfWidth;
		oddFieldVPtr += halfWidth;
	}
#endif

	return 0;
}

int DDrawH264Play::Snapshot(char *buf, int bufLen)
{
	if (m_currFrameBuf == NULL || !m_hasDecodeOneFrame)
		return -1;

	LPBITMAPINFOHEADER bmpHdr = (LPBITMAPINFOHEADER) buf;
	memset(bmpHdr, 0, sizeof(BITMAPINFOHEADER));
	bmpHdr->biSize = sizeof(BITMAPINFOHEADER);

#ifdef SUPPORT_H264
	bmpHdr->biWidth  = m_decodeWidth;
	bmpHdr->biHeight = m_decodeHeight;

	if (m_isInterlaced && bmpHdr->biHeight == 256)
		bmpHdr->biHeight = 240;

#else
	bmpHdr->biWidth  = m_demandWidth;
	bmpHdr->biHeight = m_demandHeight;
#endif

	bmpHdr->biCompression = BI_RGB;
	bmpHdr->biBitCount = 32;
	bmpHdr->biPlanes = 1;
	bmpHdr->biSizeImage = GetBitmapSize(bmpHdr);

	int height = abs(m_decodeHeight);
	int offset = m_decodeWidth * height;

	BYTE *y, *v, *u, *frameBuffer = NULL;
	if (m_isInterlaced)
	{
		frameBuffer = new BYTE[m_videoBufSize + 10240];
		y = frameBuffer;
		v = frameBuffer + offset;
		u = frameBuffer + offset * 5 / 4;

		AutoLock lock(m_stateLock);
		DeinterlaceFrames(frameBuffer, m_decodeWidth, m_decodeHeight);
	}
	else
	{
		y = m_currFrameBuf;
		v = m_currFrameBuf + offset;
		u = m_currFrameBuf + offset * 5 / 4;
	}

	yuv2rgb_32(y, m_decodeWidth, u, v, m_decodeWidth / 2, 
		(BYTE *) buf + sizeof(BITMAPINFOHEADER) + (bmpHdr->biHeight - 1) * WIDTHBYTES(bmpHdr->biWidth * 32), bmpHdr->biWidth, bmpHdr->biHeight, -1 * WIDTHBYTES(bmpHdr->biWidth * 32));

	if (frameBuffer != NULL)
		delete[] frameBuffer;

	return sizeof(BITMAPINFOHEADER) + bmpHdr->biSizeImage;
}

void DDrawH264Play::AdjustVideoSize(int newWidth, int newHeight)
{
	// TODO:
}

void DDrawH264Play::SetDataProvider(PLAYBACKSESSION session, 
									DataProviderCallBack dataProvider, 
									LPVOID param) 
{ 
	AutoLock lock(m_callbackLock);

	m_playbackSession   = session; 
	m_dataProvider      = dataProvider; 
	m_dataProviderParam = param; 
	m_isFileStream      = false;
}

void DDrawH264Play::SetSourceName(const char *filename)
{
	if (filename == NULL || m_srcFile != NULL)
		return;

	strcpy(m_srcName, filename);
	m_isFileStream = true;
}

bool DDrawH264Play::GetH264Data()
{
	if (m_lastBitrateCountTime == 0)
		m_lastBitrateCountTime = timeGetTime();

	if (m_isFileStream)
	{
		//Read H264 data stream from file(only raw h264 data file)

		if (m_srcFile == NULL
			&& (m_srcFile = fopen(m_srcName, "rb")) == NULL)
		{
			return false;
		}

		m_h264DataLen = fread(m_h264DataBuf, 1, MAX_READ_FILE_SIZE, m_srcFile);
		if (feof(m_srcFile) || ferror(m_srcFile)) 
		{
			fclose(m_srcFile);
			m_srcFile = NULL;
			m_h264DataLen = 0;

			return false;
		}
	}
	else
	{
		AutoLock lock(m_callbackLock);

		if (m_dataProvider == NULL)
			return false;

		int bufSize = m_isRawYuv ? (m_demandWidth * m_demandHeight * 3 / 2 + m_demandHeight * 3) : (MAX_FRAME_SIZE - m_h264DataLen);
		if (m_isRawYuv)
			m_h264DataLen = 0;
		DWORD t1 =GetTickCount();
		int dataLen = m_dataProvider(m_playbackSession, m_dataProviderParam, m_h264DataBuf + m_h264DataLen, bufSize);
		if (dataLen < 0)
		{
			DWORD t2 = GetTickCount();
			DEBUG_TRACE(_T("callback display fail,using time[%d]\r\n"),t2-t1);
            return false;
		}
			
		else
			m_h264DataLen += dataLen;
	}

	//Calculate video stream bitrate.

	m_totalBit += m_h264DataLen * 8;
	DWORD now = timeGetTime();
	if (now < m_lastBitrateCountTime)
	{
		m_lastBitrateCountTime = now;
		m_totalBit = 0;
	}

	if (now - m_lastBitrateCountTime >= BITRATE_COUNT_INTERVAL)
	{
		m_lastBitrate = (DWORD)(double(m_totalBit) * 1000.0 / double(now - m_lastBitrateCountTime));
		m_lastBitrateCountTime = now;
		m_totalBit = 0;
	}

	return true;
}

int DDrawH264Play::GetBitrate()
{
	return (timeGetTime() - m_lastBitrateCountTime > 2 * BITRATE_COUNT_INTERVAL)
			? 0 : m_lastBitrate;
}

unsigned int WINAPI DDrawH264Play::DecodeThreadProc(LPVOID param)
{
    DDrawH264Play &theObj =*reinterpret_cast<DDrawH264Play*>(param);
	return theObj.DecodeThreadImpl();
}

unsigned int WINAPI DDrawH264Play::PlayThreadProc(LPVOID param)
{
	return 0;
	//DDrawH264Play &theObj = *reinterpret_cast<DDrawH264Play *>(param);
   // theObj.DecodeThreadImpl();
	//return theObj.PlayThreadImpl();
}

DWORD DDrawH264Play::DecodeThreadImpl()
{
	
	while(m_playThreadCtrlFlag)
	{
		int offset = abs(m_demandHeight*m_demandWidth);
		if (m_decoderSession == 0)
		{
			m_decoderSession = CreateDecodeSessionNoSize(1);
		}
		if (m_lastFpsCountTime == 0)
		{
			m_lastFpsCountTime = timeGetTime();
		}

		if (m_frameBuf1 == NULL)
		{
			AllocateVideoBuffers();
		}
#ifdef NON_INTERLACED_FOR_CIF
		else if(m_hasDecodeOneFrame&& m_isInterlaced && m_decodeHeight <480)
		{
			ReleaseVideoBuffers();
			AllocateVideoBuffers();
		}
#endif
		else if (m_hasDecodeOneFrame 
			&& ((int) m_decodeWidth > m_demandWidth || abs(m_decodeHeight) > m_demandHeight) 
			&& m_decodeCallback == NULL
			&& (int) m_videoBufSize < m_decodeWidth * abs(m_decodeHeight) * 3 / 2)
		{
			ReleaseVideoBuffers();
			AllocateVideoBuffers();
		}

		m_currFrameBuf =m_frameBuf1;


		int getframe =0;
		DWORD startTime = timeGetTime();
	    DWORD now = timeGetTime();
		if(now<startTime)
			startTime = now;
		
		if (m_h264DataLen <= 0)  
		{
			if (!GetH264Data())
			{
				m_h264DataLen = 0;
				CloseDecodeSession(m_decoderSession);
				m_decoderSession = 0;
				m_hasDecodeOneFrame =0;

				return 0;
			}
			else if (m_h264DataLen == 0)
			{
				Sleep(6);
				continue;
			}
		}
        BYTE *inBuf = m_h264DataBuf;

		while(m_h264DataLen >0)
		{
			if (m_beginNewFrame)
			{
				UINT bytesToCopy = min(m_bytesForHeader,m_h264DataLen);
				memcpy(((char*)&m_header) + sizeof(BtlFrameHeader) - m_bytesForHeader,inBuf,bytesToCopy );
				m_bytesForHeader -= bytesToCopy;
				inBuf += bytesToCopy;

				if (m_h264DataLen <= m_bytesForHeader)
				{
					break;
				}
				m_beginNewFrame = false;
			}
			BYTE *ybuf = m_currFrameBuf,  *ubuf = m_currFrameBuf+ offset*5/4, *vbuf = m_currFrameBuf + offset;

			int decodelen = DecodeData(m_decoderSession,inBuf,m_h264DataLen,&getframe,ybuf,ubuf,vbuf,m_decodeWidth,0);
			if (decodelen <0)
			{
				m_h264DataLen =0;
				CloseDecodeSession(m_decoderSession);
				m_decoderSession = 0;
				m_hasDecodeOneFrame = false;
				ReleaseVideoBuffers();
				getframe = 0;
				m_decoderSession = CreateDecodeSessionNoSize(1);
				GetH264Data();
				m_beginNewFrame = true;
				m_bytesForHeader = sizeof(BtlFrameHeader);

			}
			if (decodelen > 0)
			{
				inBuf += decodelen;
				m_h264DataLen -= decodelen;
				m_header.videoLen -=decodelen;
				if (m_header.videoLen <= 0)
				{
					m_beginNewFrame = true;
					m_bytesForHeader =sizeof(BtlFrameHeader);
				}	
			}
			if (getframe)
			{
				if (m_isInterlaced)
				{
					std::swap(m_frameBuf1,m_frameBuf2);
				}
				if (m_h264DataLen >0)
				{
					GetVideoSize(m_decoderSession,(int *)&m_decodeWidth, (int *)&m_decodeHeight);
					if (m_currFrameBuf)
					{
						m_frameQueue.PushRawFrameData(m_currFrameBuf,m_videoBufSize,m_decodeHeight,m_demandWidth,m_header.bIFrame,m_header.timeTick);
					}	
                    
				}
				m_hasDecodeOneFrame = TRUE;
				//calculate fps
				++m_totalFrame;
				DWORD currTime = timeGetTime();
				if (currTime < m_lastFpsCountTime)
				{
					m_lastFpsCountTime =currTime;
					m_totalFrame = 0;
				}

				if (currTime - m_lastFpsCountTime >= BITRATE_COUNT_INTERVAL)
				{
					m_lastActualFps = (DWORD)(double(m_totalFrame)* 1000.0/ double(currTime -m_lastFpsCountTime)+0.5);
					m_lastFpsCountTime = currTime;
					m_totalFrame = 0;
				}
			}
		}

	}

	return 0;


}
#if 0
DWORD DDrawH264Play::PlayThreadImpl()
{
	int dispWidth  = m_demandWidth;
	int dispHeight = m_demandHeight;
	DWORD lastFrameTime = 0;
	BYTE *deinterlaceBuf = NULL;

	m_h264DataLen = 0;
	m_decoderSession = 0;
	m_hasDecodeOneFrame = false;
	m_beginNewFrame = true;
	m_bytesForHeader = sizeof(BtlFrameHeader);
	m_h264DataLen = 0;

	CoInitialize(NULL); //for DirectDraw

	CDirectDisplay *&ddrawDisplay = m_pActiveDirectDisplay;
	if (m_videoWnd)
	{
		UpdateActiveDirectDisplay();
		if (!ddrawDisplay->InitDirectDraw(m_videoWnd, dispWidth, dispHeight, 0))
			ddrawDisplay->InitDirectDraw(m_videoWnd, dispWidth, dispHeight, 0);
		ddrawDisplay->SetDisplayClipper(m_videoWnd);
		ddrawDisplay->FillDisplay(0); //blank frame
	}

	//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

	//Thread Loop
	while (m_playThreadCtrlFlag) 
	{ 
		
		if (m_fifoFrame.GetCount()== 0)
		{
			Sleep(100);
			continue;
		}
		//limit the playback speed and CPU usage
		if (m_demandFps > 0)
		{
			DWORD now = timeGetTime();
			if (now >= lastFrameTime)
			{
				if (now - lastFrameTime < max(4, 1000 / m_demandFps) - 4)
				{
					Sleep(max(4, 1000 / m_demandFps) - 4 - (now - lastFrameTime));
					now = timeGetTime();
				}
			}
			lastFrameTime = now;
		}
		else
		{
			if (m_isRawYuv)
				Sleep(10);
		}

		UpdateActiveDirectDisplay();
		if(ddrawDisplay == NULL)
		{
			Sleep(1);
			continue;
		}

        
		BYTE *videoBuf = NULL;
		if (!m_isRawYuv)
		{
#ifdef SUPPORT_H264
		//	if (!DecodeH264Frame())
		//		break;


			AutoLock lock(m_stateLock);
#if 0
			if (m_decodeCallback != NULL)
			{
				//Not display the frame,just send out by callback
				if (m_currFrameBuf != NULL)
					m_decodeCallback(m_playbackSession, reinterpret_cast<char *>(m_currFrameBuf), m_videoBufSize, m_decodeWidth, m_decodeHeight, m_decodeWidth, m_decodeCallbackParam);

				if (!m_needDisplay)
					continue;
			}
#endif

			if (m_videoWnd == NULL)
			{
				if (m_videoWndUpdateFlag)
				{
					if(ddrawDisplay->IsIntialized())
					{
						ddrawDisplay->ReleaseObject();
					}
					m_videoWndUpdateFlag = false;
				}
				m_videoRectUpdateFlag = false;

				continue;
			}
			else
			{
				if(ddrawDisplay->IsIntialized())
				{
					//RECT newWndRect;
					//GetWindowRect(m_videoWnd, &newWndRect);

					if (m_videoWndUpdateFlag)
					{
						ddrawDisplay->SetDisplayClipper(m_videoWnd);
						//ddrawDisplay->UpdataImage();
					}
					//else if (newWndRect.left != videoWndRect.left 
					//		|| newWndRect.top != videoWndRect.top)
					//{
					//	videoWndRect = newWndRect;
					//	ddrawDisplay->UpdateBounds();
					//	ddrawDisplay->UpdataImage();
					//}

					//if (m_videoRectUpdateFlag)
					//{
					//	ddrawDisplay->UpdateBounds();
					//	ddrawDisplay->UpdataImage();
					//}
				}
				else
				{
					if (!ddrawDisplay->InitDirectDraw(m_videoWnd, dispWidth, dispHeight, 0))
						ddrawDisplay->InitDirectDraw(m_videoWnd, dispWidth, dispHeight, 0);
					ddrawDisplay->SetDisplayClipper(m_videoWnd);
					ddrawDisplay->FillDisplay(0); //blank frame
				}
			}

			m_videoWndUpdateFlag = false;
			m_videoRectUpdateFlag = false;

			//Check if the video size should be changed.

			if (nCnt == 1)
			{
				int newWidth  = pRawYuvFrame->nDispWidth;
				int newHeight = pRawYuvFrame->nDispHeight;

				if (m_isInterlaced && newHeight == 256) //for TN86-21D,the 16 bottom scanline are blank.
					newHeight = 240;

				if (newWidth != dispWidth || newHeight != dispHeight)
				{
					dispWidth  = newWidth;
					dispHeight = newHeight;
					if(ddrawDisplay->IsIntialized())
					{
						ddrawDisplay->ReleaseObject();
					}
					
					if (!ddrawDisplay->InitDirectDraw(m_videoWnd, dispWidth, dispHeight, 0))
						ddrawDisplay->InitDirectDraw(m_videoWnd, dispWidth, dispHeight, 0);
					ddrawDisplay->SetDisplayClipper(m_videoWnd);
					ddrawDisplay->FillDisplay(0); //blank frame
				}
			}

			if (m_isInterlaced)
			{
				if (m_frameBuf1 != NULL)
				{
					if (deinterlaceBuf == NULL)
						deinterlaceBuf = new BYTE[m_videoBufSize];

					DeinterlaceFrames(deinterlaceBuf, m_decodeWidth, m_decodeHeight);
					videoBuf = deinterlaceBuf;
				}
			}
			else
			{
				videoBuf = pRawYuvFrame->pData;
			}
#endif //SUPPORT_H264
		}
		else
		{
			GetH264Data();
			videoBuf = m_h264DataBuf;
			if(!m_hasDecodeOneFrame)
			{
				m_currFrameBuf = m_h264DataBuf;
				m_hasDecodeOneFrame = true;
				m_decodeWidth = dispWidth;
				m_decodeHeight = dispHeight;
			}
		}

		if (m_videoWnd == NULL || !ddrawDisplay->IsIntialized() || videoBuf == NULL)
			continue;

		int ybufSize = dispWidth * dispHeight;
		ddrawDisplay->DrawImageFormYUVBuf(videoBuf, videoBuf + ybufSize * 5 / 4, videoBuf + ybufSize);

		//User can draw something on video now(if he wants).

		if (m_drawCallback != NULL && m_videoWnd != NULL)
		{
			HDC hdc = GetDC(m_videoWnd);
			m_drawCallback(m_playbackSession, hdc, m_drawCallbackParam);
			ReleaseDC(m_videoWnd, hdc);
		}

		
	} 

	struct ReleaseDirectDisplay
	{
		void operator()(CDirectDisplayMap::value_type &_val)
		{
			_val.second->ReleaseObject();
		}
	};

	for_each(m_directDisplayMap.begin(), m_directDisplayMap.end(), ReleaseDirectDisplay());

	ReleaseVideoBuffers();
	SafeDeleteArray(deinterlaceBuf);
	if (m_decoderSession != 0)
	{
		CloseDecodeSession(m_decoderSession);
		m_decoderSession = 0;
	}

	CoUninitialize();
	
	return 0;
}
#endif

int DDrawH264Play::StartPlay()
{
	AutoLock lock(m_stateLock);

	if (m_playThread != NULL)
		return 0;

	m_playThreadCtrlFlag = 1;
	m_pauseFlag = false;
	m_frameQueue.Initialize();

	m_decodeThread = reinterpret_cast<HANDLE>(_beginthreadex(NULL,0,DecodeThreadProc,this,0,NULL));
	InitPlay();

	//m_playThread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, PlayThreadProc, this, 0, NULL));

	return (m_playThread != NULL) ? 0 : -1;
}

int DDrawH264Play::StopPlay()
{
	{
		AutoLock lock(m_stateLock);

		m_playThreadCtrlFlag = 0;
		m_pauseFlag = false;
	}
	if (m_decodeThread != NULL)
	{
		WaitForSingleObject(m_decodeThread,INFINITE);
		CloseHandle(m_decodeThread);
		m_decodeThread = NULL;
	}
     
	m_frameQueue.Close(EndGetFrameProc,this);
	/*if (m_playThread != NULL)
	{
		WaitForSingleObject(m_playThread, INFINITE);
		CloseHandle(m_playThread);
		m_playThread = NULL;
	}*/

	return 0;
}

int DDrawH264Play::PausePlay()
{
	m_pauseFlag = true;

	return 0;
}

void DDrawH264Play::EnumDirectDisplay()
{
	DirectDrawEnumerateEx(DDEnumMonitorsProc, this, DDENUM_ATTACHEDSECONDARYDEVICES);
}

BOOL WINAPI DDrawH264Play::DDEnumMonitorsProc(
									  GUID FAR *lpGUID,    
									  LPSTR     lpDriverDescription, 
									  LPSTR     lpDriverName,        
									  LPVOID    lpContext,           
									  HMONITOR  hm        
									  )
{
	DDrawH264Play* pThis = reinterpret_cast<DDrawH264Play *>(lpContext);
	if(hm)
	{
		pThis->m_directDisplayMap.insert(std::make_pair(hm, new CDirectDisplay(hm, lpGUID)));
	}

	return TRUE;
}

void DDrawH264Play::UpdateActiveDirectDisplay()
{
	DWORD dwCurTimeTick = timeGetTime();
	if((dwCurTimeTick - m_dwLastCheckMonitorTick) >1000 || m_pActiveDirectDisplay == NULL)
	{
		m_dwLastCheckMonitorTick = dwCurTimeTick;
		HMONITOR hm = MonitorFromWindow(m_videoWnd, MONITOR_DEFAULTTOPRIMARY);
		HMONITOR hCurMonitor = m_pActiveDirectDisplay?m_pActiveDirectDisplay->GetMonitorHandle():NULL;

		if(hm && hm != hCurMonitor)
		{
			CDirectDisplayMap::const_iterator it = m_directDisplayMap.find(hm);
			if(it != m_directDisplayMap.end())
			{
				m_pActiveDirectDisplay = it->second.get();
			}
			else
			{
				CDirectDisplay* pDirectDisplay = new CDirectDisplay(hm);
				m_directDisplayMap.insert(std::make_pair(hm, pDirectDisplay));
				m_pActiveDirectDisplay = pDirectDisplay;
			}
		}
	}
	
	
}

BOOL DDrawH264Play::StartGetFrameThread()
{
	if (m_frameQueue.StartGetFrameThread(GetFrameDataProc,this))
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

QUEUE_ACTION DDrawH264Play::GetFrameDataProc(unsigned char* pData,int nSize,__int64 timeStamp, void* pUserData)
{
	DDrawH264Play* pThis = static_cast<DDrawH264Play*> (pUserData);
	//the data has ready for Display
	AutoLock lock(pThis->m_stateLock);
	
	if (pThis->m_videoWnd == NULL)
	{
		if (pThis->m_videoWndUpdateFlag)
		{
			if(pThis->m_pActiveDirectDisplay->IsIntialized())
			{
					pThis->m_pActiveDirectDisplay->ReleaseObject();
			}
				pThis->m_videoWndUpdateFlag = false;
		}
		pThis->m_videoRectUpdateFlag = false;

	}
	else
	{
		if(pThis->m_pActiveDirectDisplay->IsIntialized())
		{
				//RECT newWndRect;
				//GetWindowRect(m_videoWnd, &newWndRect);

			if (pThis->m_videoWndUpdateFlag)
			{
				pThis->m_pActiveDirectDisplay->SetDisplayClipper(pThis->m_videoWnd);
					//ddrawDisplay->UpdataImage();
			}
				//else if (newWndRect.left != videoWndRect.left 
				//		|| newWndRect.top != videoWndRect.top)
				//{
				//	videoWndRect = newWndRect;
				//	ddrawDisplay->UpdateBounds();
				//	ddrawDisplay->UpdataImage();
				//}

				//if (m_videoRectUpdateFlag)
				//{
				//	ddrawDisplay->UpdateBounds();
				//	ddrawDisplay->UpdataImage();
				//}
		}
		else
		{
			if (!pThis->m_pActiveDirectDisplay->InitDirectDraw(pThis->m_videoWnd, pThis->dispWidth, pThis->dispHeight, 0))
					pThis->m_pActiveDirectDisplay->InitDirectDraw(pThis->m_videoWnd, pThis->dispWidth, pThis->dispHeight, 0);
			pThis->m_pActiveDirectDisplay->SetDisplayClipper(pThis->m_videoWnd);
			pThis->m_pActiveDirectDisplay->FillDisplay(0); //blank frame
		}
	}

	pThis->m_videoWndUpdateFlag = false;
	pThis->m_videoRectUpdateFlag = false;
		//check if the video size should be changed 
	int newWidth = pThis->m_decodeWidth;
	int newHeight = pThis->m_decodeHeight;
	if (pThis->m_isInterlaced&&newHeight==256)
	{
		newHeight = 240;
	}
	if (newHeight != pThis->dispWidth || newHeight != pThis->dispHeight)
	{
		pThis->dispHeight = newHeight;
		pThis->dispWidth = newWidth;
	   if (pThis->m_pActiveDirectDisplay->IsIntialized())
		{
			pThis->m_pActiveDirectDisplay->ReleaseObject();
		}
		if (!pThis->m_pActiveDirectDisplay->InitDirectDraw(pThis->m_videoWnd,pThis->dispWidth,pThis->dispHeight,0))
                  pThis->m_pActiveDirectDisplay->InitDirectDraw(pThis->m_videoWnd,pThis->dispWidth,pThis->dispHeight,0);
		pThis->m_pActiveDirectDisplay->SetDisplayClipper(pThis->m_videoWnd);
		pThis->m_pActiveDirectDisplay->FillDisplay(0);
	}

		
	
	if (pThis->m_videoWnd == NULL||!pThis->m_pActiveDirectDisplay->IsIntialized()||pData == NULL)
	{
		return ACTION_GET_NEXT_FRAME;
	}

	int yBufSize = pThis->dispHeight* pThis->dispWidth;
	pThis->m_pActiveDirectDisplay->DrawImageFormYUVBuf(pData, pData + yBufSize*5/4, pData + yBufSize);

	return ACTION_GET_NEXT_FRAME;

}

void DDrawH264Play::InitPlay()
{
	dispHeight = m_decodeHeight;
	dispWidth = m_demandWidth;
	m_h264DataLen = 0;
	m_decoderSession = 0;

	m_hasDecodeOneFrame = false;
	m_beginNewFrame = true;
	m_bytesForHeader = sizeof(BtlFrameHeader);
	if (m_videoWnd)
	{
		UpdateActiveDirectDisplay();
		if (!m_pActiveDirectDisplay->InitDirectDraw(m_videoWnd,dispHeight,dispWidth,0))
		{
			m_pActiveDirectDisplay->InitDirectDraw(m_videoWnd,dispHeight,dispWidth,0);
		}
		m_pActiveDirectDisplay->SetDisplayClipper(m_videoWnd);
		m_pActiveDirectDisplay->FillDisplay(0);
	}
	BOOL bResult = m_frameQueue.StartGetFrameThread(GetFrameDataProc,this);
}


void DDrawH264Play::EndGetFrameProc(void* pUserData)
{
	DDrawH264Play* pThis = static_cast<DDrawH264Play*>(pUserData);
	pThis->CloseVideo();
	pThis->StopGetFramethread();

}
void DDrawH264Play::CloseVideo()
{
	struct RealeaseDirectDisplay 
	{
		void operator()(CDirectDisplayMap::value_type &_val)
		{
		   _val.second->ReleaseObject();  
		}
	};

	for_each(m_directDisplayMap.begin(),m_directDisplayMap.end(),RealeaseDirectDisplay());
	ReleaseVideoBuffers();
	if(m_decoderSession != 0)
	{
		CloseDecodeSession(m_decoderSession);
		m_decoderSession = 0;
	}

	CoUninitialize();
}

void DDrawH264Play::StopGetFramethread()
{
	m_frameQueue.StopGetFrameThread();
}